import Base3D from '../index'
import { WebGLRenderer, sRGBEncoding, BasicShadowMap, ACESFilmicToneMapping, ReinhardToneMapping, PCFSoftShadowMap, PCFShadowMap, Vector2 } from 'three'
import * as THREE from 'three'
import { GUI } from 'three/addons/libs/lil-gui.module.min.js'
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js'
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js'
import { ShaderPass } from 'three/addons/postprocessing/ShaderPass.js'
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js'
import { SAOPass } from 'three/addons/postprocessing/SAOPass.js'
import { SSAOPass } from 'three/addons/postprocessing/SSAOPass.js'
// import { OutputPass } from 'three/addons/postprocessing/OutputPass.js'
import { VignetteShader } from 'three/addons/shaders/VignetteShader.js'
import { GammaCorrectionShader } from 'three/addons/shaders/GammaCorrectionShader.js'

const postprocessingOn = true
const darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' })
export default class Renderer {
  constructor() {
    this.base3D = Base3D.getInstance()
    this.size = this.base3D.size
    this.scene = this.base3D.scene
    this.canvas = this.base3D.canvas
    this.camera = this.base3D.camera
    this.unrealBloomPassParams = {
      threshold: 0,
      strength: 1,
      radius: 0.5,
      exposure: 1,
    }
    this.init()
  }
  init() {
    console.error('init  Renderer========>')
    this.instance = new WebGLRenderer({
      canvas: this.canvas,
      antialias: true,
      alpha: true,
      logarithmicDepthBuffer: false,
      powerPreference: 'high-performance',
    })
    this.instance.physicallyCorrectLights = false
    // this.instance.outputEncoding = sRGBEncoding
    this.instance.shadowMap.autoUpdate = true
    // this.instance.toneMapping = ReinhardToneMapping
    // this.instance.toneMappingExposure = 1.2
    // this.instance.setClearColor(0xbfd1e5);
    this.instance.shadowMap.enabled = true
    this.instance.shadowMap.type = PCFShadowMap
    this.instance.setPixelRatio(this.size.pixelRatio)
    this.instance.setSize(this.size.width, this.size.height)
    this.instance.toneMapping = THREE.ReinhardToneMapping
    if (postprocessingOn) {
      this.setPostProcessing()
    }
  }

  setPostProcessing() {
    const renderScene = new RenderPass(this.scene, this.camera.instance)
    const ssaoPass = new SSAOPass(this.scene, this.camera.instance, window.innerWidth, window.innerHeight)
    ssaoPass.kernelRadius = 0.01
    ssaoPass.minDistance = 0.0001

    // const outputPass = new OutputPass()
    const mixPass = this.initUnrealBloomPass(renderScene)
    this.composer = new EffectComposer(this.instance)
    this.composer.addPass(renderScene)
    // this.composer.addPass(outputPass)
    this.composer.addPass(ssaoPass)
    this.composer.addPass(mixPass)
  }
  // 辉光
  initUnrealBloomPass(renderScene) {
    this.BLOOM_SCENE = 1
    this.bloomLayer = new THREE.Layers()
    this.bloomLayer.set(this.BLOOM_SCENE)
    this.materials = {}
    const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85)
    bloomPass.threshold = 0
    bloomPass.strength = 0.5
    bloomPass.radius = 0.1

    this.bloomComposer = new EffectComposer(this.instance)
    this.bloomComposer.renderToScreen = false
    this.bloomComposer.addPass(renderScene)
    this.bloomComposer.addPass(bloomPass)
    const mixPass = new ShaderPass(
      new THREE.ShaderMaterial({
        uniforms: {
          baseTexture: { value: null },
          bloomTexture: { value: this.bloomComposer.renderTarget2.texture },
        },
        vertexShader: `varying vec2 vUv;
          void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
          }`,
        fragmentShader: `uniform sampler2D baseTexture;
          uniform sampler2D bloomTexture;
          varying vec2 vUv;
          void main() {
            gl_FragColor = ( texture2D( baseTexture, vUv ) + vec4( 1.0 ) * texture2D( bloomTexture, vUv ) );
          }`,
        defines: {},
      }),
      'baseTexture'
    )
    mixPass.needsSwap = true
    this.initDebugUnrealBloomPass()
    return mixPass
  }
  initDebugUnrealBloomPass() {
    const gui = new GUI()
    const bloomFolder = gui.addFolder('bloom')
    bloomFolder.add(this.unrealBloomPassParams, 'threshold', 0.0, 1.0)
    bloomFolder.add(this.unrealBloomPassParams, 'strength', 0.0, 3)
    bloomFolder.add(this.unrealBloomPassParams, 'radius', 0.0, 1.0).step(0.01)
    const toneMappingFolder = gui.addFolder('tone mapping')
    toneMappingFolder.add(this.unrealBloomPassParams, 'exposure', 0.1, 2)
  }

  resize() {
    this.instance.setSize(this.size.width, this.size.height)
    this.instance.setPixelRatio(Math.min(this.size.pixelRatio, 2))
    if (this.composer) {
      this.composer.setSize(this.size.width, this.size.height)
      this.bloomComposer.setSize(this.size.width, this.size.height)
    }
  }
  update() {
    let that = this
    // console.log(this.instance.info.render.calls)
    if (!postprocessingOn) {
      this.instance.render(this.scene, this.camera.instance)
    } else {
      if (this.composer) {
        this.scene.traverse(this.darkenNonBloomed.bind(this))
        this.bloomComposer.render()
        this.scene.traverse(this.restoreMaterial.bind(this))
        this.composer.render()
      }
    }
  }
  darkenNonBloomed(obj) {
    if (obj.isMesh) {
      if (this.bloomLayer.test(obj.layers) === false) {
        this.materials[obj.uuid] = obj.material
        obj.material = darkMaterial
      }
    }
  }
  restoreMaterial(obj) {
    if (this.materials[obj.uuid]) {
      obj.material = this.materials[obj.uuid]
      delete this.materials[obj.uuid]
    }
  }
}
